package com.icongyou.enterprise.talent_pool.service;

import com.icongyou.enterprise.data_aggregation.entity.AssignmentEntity;
import com.icongyou.enterprise.data_aggregation.service.DataAggregationService;
import com.icongyou.enterprise.talent_pool.entity.AssignmentTagEntity;
import com.icongyou.enterprise.talent_pool.mapper.AssignmentTagMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.time.LocalDateTime;
import java.util.*;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class TagRelevanceCalculationServiceTest {

    @Mock
    private AssignmentTagMapper assignmentTagMapper;

    @Mock
    private AIModelService aiModelService;

    @Mock
    private DataAggregationService dataAggregationService;

    @Mock
    private AssignmentWeightService assignmentWeightService;

    @InjectMocks
    private TagRelevanceCalculationService tagRelevanceCalculationService;

    @Captor
    private ArgumentCaptor<List<AssignmentTagEntity>> assignmentTagsCaptor;

    private List<AssignmentEntity> mockAssignments;
    private final String TEST_TAG_ID = "test-tag-123";
    private final String TEST_TAG_NAME = "Java编程";

    @BeforeEach
    void setUp() {
        // 创建模拟的截止任务数据
        mockAssignments = Arrays.asList(
                createMockAssignment("assignment-1", "Java编程作业：实现一个简单的学生管理系统"),
                createMockAssignment("assignment-2", "Python数据分析项目"),
                createMockAssignment("assignment-3", "Web前端开发：React框架应用"),
                createMockAssignment("assignment-4", "Java多线程编程练习"),
                createMockAssignment("assignment-5", "数据库设计与SQL优化")
        );
    }

    @Test
    void calculateTagForAllAssignments_Success() {
        // 准备数据
        when(dataAggregationService.getAllCompletedAssignments()).thenReturn(mockAssignments);

        // 模拟AI服务返回关联度
        Map<String, Double> batchRelevanceMap = new HashMap<>();
        batchRelevanceMap.put("assignment-1", 0.8);
        batchRelevanceMap.put("assignment-2", 0.3);
        batchRelevanceMap.put("assignment-3", 0.2);
        batchRelevanceMap.put("assignment-4", 0.7);
        batchRelevanceMap.put("assignment-5", 0.4);

        when(aiModelService.batchCalculateTagRelevance(eq(TEST_TAG_NAME), anyMap()))
                .thenReturn(batchRelevanceMap);

        // 模拟权重服务
        when(assignmentWeightService.calculateAssignmentImportanceWeight(anyString()))
                .thenReturn(1.0);

        // 执行方法
        tagRelevanceCalculationService.calculateTagForAllAssignments(TEST_TAG_ID, TEST_TAG_NAME);

        // 验证
        verify(dataAggregationService).getAllCompletedAssignments();
        verify(aiModelService, atLeastOnce()).batchCalculateTagRelevance(eq(TEST_TAG_NAME), anyMap());
        verify(assignmentTagMapper, atLeastOnce()).batchInsertAssignmentTags(assignmentTagsCaptor.capture());

        // 验证保存的AssignmentTagEntity
        List<AssignmentTagEntity> savedTags = assignmentTagsCaptor.getValue();
        assertNotNull(savedTags);
        assertEquals(5, savedTags.size()); // 应该有5个任务标签关联

        // 验证其中一个AssignmentTagEntity的属性
        AssignmentTagEntity firstTag = savedTags.stream()
                .filter(tag -> "assignment-1".equals(tag.getAssignmentId()))
                .findFirst()
                .orElse(null);

        assertNotNull(firstTag);
        assertEquals(TEST_TAG_ID, firstTag.getTagId());
        assertEquals(0.8, firstTag.getRelevance());
        assertEquals(1.0, firstTag.getAssignmentWeight());
        assertNotNull(firstTag.getId());
        assertNotNull(firstTag.getCalculatedTime());
    }

    @Test
    void calculateTagForAllAssignments_EmptyAssignments() {
        // 准备数据 - 空任务列表
        when(dataAggregationService.getAllCompletedAssignments()).thenReturn(Collections.emptyList());

        // 执行方法
        tagRelevanceCalculationService.calculateTagForAllAssignments(TEST_TAG_ID, TEST_TAG_NAME);

        // 验证
        verify(dataAggregationService).getAllCompletedAssignments();
        verify(aiModelService, never()).batchCalculateTagRelevance(anyString(), anyMap());
        verify(assignmentTagMapper, never()).batchInsertAssignmentTags(anyList());
    }

    @Test
    void calculateTagForAllAssignments_Exception() {
        // 准备数据 - 模拟异常
        when(dataAggregationService.getAllCompletedAssignments())
                .thenThrow(new RuntimeException("数据库连接失败"));

        // 执行并验证异常
        RuntimeException exception = assertThrows(RuntimeException.class, () -> {
            tagRelevanceCalculationService.calculateTagForAllAssignments(TEST_TAG_ID, TEST_TAG_NAME);
        });

        assertEquals("计算标签与所有任务关联度失败", exception.getMessage());
    }

    @Test
    void calculateRelevanceByKeyword_ExactMatch() {
        // 测试完全匹配
        Double relevance = tagRelevanceCalculationService.calculateRelevanceByKeyword(
                "Java", "这是一个关于Java编程的作业");

        assertEquals(0.8, relevance, 0.001);
    }

    @Test
    void calculateRelevanceByKeyword_PartialMatch() {
        // 测试分词匹配 - 使用更宽松的断言
        Double relevance = tagRelevanceCalculationService.calculateRelevanceByKeyword(
                "Java 编程", "学习Java语言和编程技巧");

        // 由于算法简化，我们只验证有合理的关联度即可
        assertTrue(relevance > 0.0, "Should have some relevance, but got: " + relevance);
        assertTrue(relevance <= 0.8, "Should not exceed maximum relevance, but got: " + relevance);
    }

    @Test
    void calculateRelevanceByKeyword_NoMatch() {
        // 测试无匹配
        Double relevance = tagRelevanceCalculationService.calculateRelevanceByKeyword(
                "人工智能", "这是一个关于数据库的作业");

        assertEquals(0.0, relevance, 0.001);
    }

    @Test
    void calculateRelevanceByKeyword_EmptyText() {
        // 测试空文本
        Double relevance = tagRelevanceCalculationService.calculateRelevanceByKeyword(
                "Java", "");

        assertEquals(0.0, relevance, 0.001);
    }

    @Test
    void calculateRelevanceByKeyword_NullText() {
        // 测试null文本
        Double relevance = tagRelevanceCalculationService.calculateRelevanceByKeyword(
                "Java", null);

        assertEquals(0.0, relevance, 0.001);
    }

    @Test
    void calculateRelevanceByKeyword_SimpleChineseMatching() {
        // 简单中文匹配测试 - 使用空格分隔的中文标签
        Double relevance = tagRelevanceCalculationService.calculateRelevanceByKeyword(
                "数据库 设计", "本次作业要求完成数据库设计和SQL优化");

        // 使用更宽松的断言
        assertTrue(relevance > 0.0, "Should have some relevance for Chinese matching, but got: " + relevance);
    }
//
//    @Test
//    void calculateRelevanceByKeyword_EdgeCases() {
//        // 测试边界情况
//
//        // 空标签
//        Double emptyTag = tagRelevanceCalculationService.calculateRelevanceByKeyword("", "一些文本");
//        assertEquals(0.0, emptyTag, 0.001);
//
//        // 只有一个字符的标签 - 现在会被跳过
//        Double singleChar = tagRelevanceCalculationService.calculateRelevanceByKeyword("编", "编程学习");
//        assertEquals(0.0, singleChar, 0.001, "Single character should be skipped, but got: " + singleChar);
//
//        // 全是标点符号
//        Double punctuation = tagRelevanceCalculationService.calculateRelevanceByKeyword("，。；", "文本");
//        assertEquals(0.0, punctuation, 0.001);
//    }

    @Test
    void calculateRelevanceByKeyword_WordLengthFilter() {
        // 测试词汇长度过滤
        Double shortWord = tagRelevanceCalculationService.calculateRelevanceByKeyword(
                "A B C", "文本包含A和B和C");
        assertEquals(0.0, shortWord, 0.001, "Single character words should be filtered out, but got: " + shortWord);

        Double mixedWords = tagRelevanceCalculationService.calculateRelevanceByKeyword(
                "Java 编程", "Java编程学习");
        assertTrue(mixedWords > 0.0, "Should match valid words and ignore single characters, but got: " + mixedWords);
    }

    @Test
    void calculateRelevanceByKeyword_MixedLanguage() {
        // 测试混合语言匹配
        Double relevance = tagRelevanceCalculationService.calculateRelevanceByKeyword(
                "Java 编程 Database", "学习Java编程和数据库设计");

        assertTrue(relevance > 0.0, "Should handle mixed language tags, but got: " + relevance);
        assertTrue(relevance <= 0.8, "Should not exceed maximum relevance, but got: " + relevance);
    }

    @Test
    void calculateRelevanceByKeyword_SimpleTest() {
        // 简单的确定性测试
        // 完全匹配
        assertEquals(0.8, tagRelevanceCalculationService.calculateRelevanceByKeyword("test", "this is a test"), 0.001);

        // 多词部分匹配
        Double multiWord = tagRelevanceCalculationService.calculateRelevanceByKeyword("Java 编程", "Java语言学习");
        assertTrue(multiWord > 0.0 && multiWord < 0.8, "Multi-word partial match should return reasonable score, but got: " + multiWord);

        // 无匹配
        assertEquals(0.0, tagRelevanceCalculationService.calculateRelevanceByKeyword("none", "this is a test"), 0.001);
    }

    @Test
    void saveAssignmentTagsBatch_Success() {
        // 准备数据
        List<AssignmentTagEntity> assignmentTags = Arrays.asList(
                createAssignmentTagEntity("assignment-1", TEST_TAG_ID, 0.8, 1.0),
                createAssignmentTagEntity("assignment-2", TEST_TAG_ID, 0.6, 1.0)
        );

        // 执行方法
        tagRelevanceCalculationService.saveAssignmentTagsBatch(assignmentTags);

        // 验证
        verify(assignmentTagMapper).deleteByTagIdAndAssignmentIds(eq(TEST_TAG_ID), anyList());
        verify(assignmentTagMapper).batchInsertAssignmentTags(assignmentTags);
    }

    @Test
    void saveAssignmentTagsBatch_EmptyList() {
        // 执行方法 - 空列表
        tagRelevanceCalculationService.saveAssignmentTagsBatch(Collections.emptyList());

        // 验证 - 不应该调用任何数据库操作
        verify(assignmentTagMapper, never()).deleteByTagIdAndAssignmentIds(anyString(), anyList());
        verify(assignmentTagMapper, never()).batchInsertAssignmentTags(anyList());
    }

    @Test
    void saveAssignmentTagsBatch_Exception() {
        // 准备数据
        List<AssignmentTagEntity> assignmentTags = Arrays.asList(
                createAssignmentTagEntity("assignment-1", TEST_TAG_ID, 0.8, 1.0)
        );

        // 模拟数据库异常
        doThrow(new RuntimeException("数据库异常")).when(assignmentTagMapper)
                .batchInsertAssignmentTags(assignmentTags);

        // 执行并验证异常
        RuntimeException exception = assertThrows(RuntimeException.class, () -> {
            tagRelevanceCalculationService.saveAssignmentTagsBatch(assignmentTags);
        });

        assertEquals("批量保存任务标签关联失败", exception.getMessage());
    }

    @Test
    void createAssignmentTagEntity_Success() {
        // 执行方法
        AssignmentTagEntity entity = tagRelevanceCalculationService.createAssignmentTagEntity(
                "assignment-1", TEST_TAG_ID, 0.8, 1.0);

        // 验证
        assertNotNull(entity);
        assertEquals("assignment-1", entity.getAssignmentId());
        assertEquals(TEST_TAG_ID, entity.getTagId());
        assertEquals(0.8, entity.getRelevance());
        assertEquals(1.0, entity.getAssignmentWeight());
        assertNotNull(entity.getId());
        assertNotNull(entity.getCalculatedTime());
    }

    // 辅助方法：创建模拟的AssignmentEntity
    private AssignmentEntity createMockAssignment(String assignmentId, String description) {
        AssignmentEntity assignment = new AssignmentEntity();
        assignment.setAssignmentId(assignmentId);
        assignment.setDescription(description);
        return assignment;
    }

    // 辅助方法：创建AssignmentTagEntity
    private AssignmentTagEntity createAssignmentTagEntity(String assignmentId, String tagId,
                                                          Double relevance, Double weight) {
        AssignmentTagEntity entity = new AssignmentTagEntity();
        entity.setId(UUID.randomUUID().toString());
        entity.setAssignmentId(assignmentId);
        entity.setTagId(tagId);
        entity.setRelevance(relevance);
        entity.setAssignmentWeight(weight);
        entity.setCalculatedTime(LocalDateTime.now());
        return entity;
    }
}